package com.jdrx.sw.sewage.service;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jdrx.platform.commons.rest.exception.BizException;
import com.jdrx.sw.sewage.beans.entity.DeviceTimeDataPO;
import com.jdrx.sw.sewage.common.util.Asserts;
import com.jdrx.sw.sewage.common.util.LocalDateTimeUtils;
import com.jdrx.sw.sewage.dao.DeviceTimeDataDAO;
import com.jdrx.sw.sewage.service.base.BaseServiceImpl;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.jdrx.sw.sewage.common.constant.Constant.DATA_ANALYSIS_1HOUR;
import static com.jdrx.sw.sewage.common.util.SewageDaoUtils.validateCount;

@Service
public class DeviceTimeDataServiceImpl extends BaseServiceImpl {

    public static final Logger logger = LoggerFactory.getLogger(DeviceTimeDataServiceImpl.class);

    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private DeviceTimeDataDAO deviceTimeDataDao;

    @Transactional(rollbackFor = Exception.class)
    @Async("asyncServiceExecutor")
    public void handlePgData(Map<Long, Map<String, Object>> pgDataMap, String time, String dataNode, LocalDateTime now) throws BizException {
        LocalDateTime localDateTime = LocalDateTimeUtils.stringToLocalDateTime(time);
//        List<DeviceTimeDataPO> pos = Lists.newArrayList();
        List<Map<String, Object>> pos = Lists.newArrayList();
        pgDataMap.forEach((devId, map) -> {
            Map<String, Object> deviceTimeDataPO = new HashMap<>();
//            DeviceTimeDataPO deviceTimeDataPO = new DeviceTimeDataPO();
            try {
//                deviceTimeDataPO.setDevId(devId);
//                deviceTimeDataPO.setCollectTime(localDateTime);
//                deviceTimeDataPO.setCreateTime(now);
//                deviceTimeDataPO.setJsonVal(JSONObject.parseObject(objectMapper.writeValueAsString(map)));
                deviceTimeDataPO.put("devId", devId);
                deviceTimeDataPO.put("jsonVal", "'" + JSONObject.parseObject(objectMapper.writeValueAsString(map)) + "'");
                deviceTimeDataPO.put("collectTime", localDateTime);
                deviceTimeDataPO.put("createTime", now);
                pos.add(deviceTimeDataPO);
            } catch (Exception e) {
                logger.error("生成存入数据库设备信息失败", e);
            }
        });
        if (CollectionUtils.isNotEmpty(pos)) batchInsert(pos, dataNode);

    }

    private void batchInsert(List<Map<String, Object>> lists, String dataNode) throws BizException {
        int count;
        Map<String, Object> params = Maps.newHashMap();
        params.put("list", lists);
        params.put("dataNode", dataNode);
        count = deviceTimeDataDao.batchInsert(params);
    }

    public Integer sysTestCloseAlarm(Long deviceId, LocalDateTime startTime, String gatherDevPointValue, BigDecimal val, BigDecimal val2, String dataNode) {
        return deviceTimeDataDao.sysTestCloseAlarm(dataNode, deviceId, startTime, gatherDevPointValue, val, val2);
    }

    /**
     * 从实时数据表sewage_device_time_data
     * 按照指定时间间隔过滤数据到sewage_device_time_data_1min
     * 执行周期(每小时执行一次)
     *
     * @throws BizException
     */
    @Transactional(rollbackFor = Exception.class)
    public void deviceTimeDataFilter(String dataNode) throws BizException {
        if (!StringUtils.isEmpty(dataNode)) dataNode = getSQLPerfix(dataNode);

        logger.info(dataNode + "数据过滤开始，执行时间{}", LocalDateTimeUtils.localDateTime2String(LocalDateTime.now()));
        Map<String, Object> params = Maps.newHashMap();
        LocalDateTime startTime = LocalDateTime.of(LocalDate.now(), LocalTime.now().minusHours(1L).withSecond(0).withNano(0));
        LocalDateTime endTime = LocalDateTime.of(LocalDate.now(), LocalTime.now().withSecond(0).withNano(0));
        params.put("startTime", startTime);
        params.put("endTime", endTime);
        params.put("dataNode", dataNode);
        params.put("strTime", DATA_ANALYSIS_1HOUR);
        List<DeviceTimeDataPO> dataPOS = findRealTimeLastPacksByParams(params);
        // 此处走的是http请求，请求头中有租户信息，则此处租户Id可以为null
        batchInsertHistory(dataPOS, dataNode);
        logger.info(dataNode + "数据过滤执行完成，执行时间{}", LocalDateTimeUtils.localDateTime2String(LocalDateTime.now()));
    }

    public List<DeviceTimeDataPO> findRealTimeLastPacksByParams(Map<String, Object> params) throws BizException {
        Asserts.notNull(params, "数据过滤参数");
        try {
            return deviceTimeDataDao.findRealTimeLastPacksByParams(params);
        } catch (Exception e) {
            logger.error("数据过滤失败", e);
            throw new BizException("数据过滤失败");
        }
    }

    /**
     * 批量保存物联网实时数据到最后一包数据表
     * <p>
     * 可塞入dataNode以控制mycat选择数据库
     * </p>
     *
     * @param lists
     * @param dataNode 需转换成/*!mycat:schema = " + dataNode + *"
     * @throws BizException
     */
    public void batchInsertHistory(List<DeviceTimeDataPO> lists, String dataNode) throws BizException {
        int count;
        Map<String, Object> params = Maps.newHashMap();
        if (CollectionUtils.isEmpty(lists)) {
            return;
        }
        params.put("list", lists);
        params.put("dataNode", dataNode);
        try {
            count = deviceTimeDataDao.batchInsertHistory(params);
        } catch (Exception e) {
            logger.error("批量插入失败", e);
            throw new BizException("批量插入失败");
        }
        validateCount(count, lists.size(), "批量插入失败");
    }

    public List<DeviceTimeDataPO> energyReport(String dataNode, String key, String strTime, String strDay) {
        return deviceTimeDataDao.energyReport(dataNode, key, strTime, strDay);
    }

    public List<Double> selectNearbyFiveByDevId(Long devId, String gdpv, String dataNode) {
        return deviceTimeDataDao.selectNearbyFiveByDevId(devId, gdpv, dataNode);
    }
}
